package org.example.java_gobang.game;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import org.example.java_gobang.JavaGobangApplication;
import org.example.java_gobang.model.User;
import org.example.java_gobang.model.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import java.io.IOException;
import java.util.UUID;

//这个类是一个游戏房间
@Data
public class Room {

    private String roomId;   //使用字符串类型表示  ,方便生成唯一值

    private User user1;
    private User user2;

    //哪个玩家是先手方
    private int whiteUser;

    //搞两个常量
    private static final int MAX_ROW = 15;
    private static final int MAX_COL = 15;
    //搞一个二维数组来表示棋盘
    //使用 0 表示当前位置未落子, 初始化好的int二维数组 是全0的
    //使用1 表示 user1 的落子位置
    //使用2 表示 user2 的落子位置
    private int[][] board = new int[MAX_ROW][MAX_COL];

    //创建 ObjectMapper 用来转换 JSON
    private ObjectMapper objectMapper = new ObjectMapper();
    //引入OnlineUserManager
    private OnlineUserManager onlineUserManager;

    //引入 RoomManager,用于房间销毁
    //@Autowired
    private RoomManager roomManager;

    private UserMapper userMapper;

    //通过这个方法来处理一次落子请求

    public void putChess(String reqJson) throws IOException {
        // 1. 记录当前落子的位置
        GameRequest request = objectMapper.readValue(reqJson,GameRequest.class);
        GameResponse response = new GameResponse();
        //当前这个子是玩家1 落的还是玩家2 落的 根据这个玩家1 和玩家2 来决定往数组中 是写1 还是写2
        int chess = request.getUserId() == user1.getUserId() ? 1 : 2;
        int row = request.getRow();
        int col = request.getCol();
        if (board[row][col] != 0) {
            System.out.println("当前位置 (" +row + ", " + col + ") 已经有子了!");
            return;
        }
        board[row][col] = chess;

        // 2.打印出棋盘信息 , 方便服务器  验证胜负关系的判定
        printBoard();

        // 3. 进行胜负判定
        int winner = checkWinner(row,col,chess);
        // 4. 给房间所有客户端返回响应
        response.setMessage("putChess");
        response.setUserId(request.getUserId());
        response.setRow(row);
        response.setCol(col);
        response.setWinner(winner);

        //要给用户发送websocket 数据 ,就需要获取带这个用户的WebSocketSession
        WebSocketSession session1 = onlineUserManager.getFromGameRoom(user1.getUserId());
        WebSocketSession session2 = onlineUserManager.getFromGameRoom(user2.getUserId());

        //万一当前查到的会话为空  说明玩家1 掉线了  直接让玩家2 获胜
        if (session1 == null) {
            response.setWinner(user2.getUserId());
            System.out.println("玩家1 掉线!");
        }

        //同理
        if (session2 == null) {
            response.setWinner(user1.getUserId());
            System.out.println("玩家2 掉线!");
        }

        //把响应构造成 JSON 字符串 ,通过session进行传输
        String respJson = objectMapper.writeValueAsString(response);
        if (session1 != null) {
            session1.sendMessage(new TextMessage(respJson));
        }
        if (session2 != null) {
            session2.sendMessage(new TextMessage(respJson));
        }

        //5. 如果当前游戏胜负已分 ,此时 这个房间就失去了 存在的意义 ,就可以删除该房间了 (从房间管理器中移除)
        if (response.getWinner() != 0) {
            //胜负已分
            System.out.println("游戏结束! 房间即将销毁 ! roomId=" + roomId + "获胜方为: " + response.getWinner());
            //更新  获胜方  和失败方的信息
            int winUserId = response.getUserId();
            int loseUserId = response.getUserId() == user1.getUserId() ? user2.getUserId() : user1.getUserId();
            userMapper.userWin(winUserId);
            userMapper.userLose(loseUserId);
            //销毁房间
            roomManager.remove(roomId,user1.getUserId(),user2.getUserId());
        }
    }

    private void printBoard() {
        //打印出棋盘
        System.out.println("[打印棋盘信息] " + roomId);
        System.out.println("=====================================================================");
       for (int r = 0; r < MAX_ROW; r++) {
           for (int c = 0; c < MAX_COL; c++) {
               //不换行 print
               System.out.print(board[r][c] + " ");
           }
           System.out.println();
       }
        System.out.println("=====================================================================");
    }


    // 使用这个方法来判定当前落子是否分出胜负
    // 约定玩家1 获胜,就返回玩家1 饿 userId
    //如果玩家2 获胜, 就返回玩家2 的userId
    // 没有分出胜负 ,就返回0
    private int checkWinner(int row, int col, int chess) {
        //1.检查所有的行  先遍历五中情况
        for (int c = col -4; c <= col; c++) {
            //先针对其中一种情况  ,来判定这五个棋子是不是连在一起了
            //且  这五个子是同一个玩家落的  才算获胜
            try {
                if (board[row][c] == chess && board[row][c+1] == chess
                        && board[row][c+2] == chess && board[row][c+3] == chess
                        && board[row][c+4] == chess) {
                    //这样就构成了五子连珠   获胜
                    return chess == 1 ? user1.getUserId() : user2.getUserId();
                }
            }catch (ArrayIndexOutOfBoundsException e) {
                //如果数组下标越界  ,直接可以忽略这个异常
                continue;
            }

        }

        //2. 检查所有列
        for (int r = row - 4; r <= row; r++) {
            try {
               if (board[r][col] == chess && board[r+1][col] == chess
                   && board[r+2][col] == chess && board[r+3][col] == chess
                   && board[r+4][col] == chess) {
                   return chess == 1 ? user1.getUserId() : user2.getUserId();
               }
            }catch (ArrayIndexOutOfBoundsException e) {
                continue;
            }
        }

        // 3.检查左对角线
        for (int r = row - 4, c = col -4; r <= row && c <= col; r++,c++) {
            try {
                if (board[r][c] == chess && board[r + 1][c + 1] == chess
                        && board[r + 2][c + 2] == chess && board[r + 3][c + 3] == chess
                        && board[r + 4][c + 4] == chess) {
                    return chess == 1 ? user1.getUserId() : user2.getUserId();
                }
            } catch (ArrayIndexOutOfBoundsException e) {
                continue;
            }
        }

        //检查右对角线
        for (int r = row -4,c = col+4; r <= row && c >=col; r++,c--) {
            try {
                if (board[r][c] == chess && board[r+1][c-1] == chess
                    && board[r+2][c-2] == chess && board[r+3][c-3] == chess
                    && board[r+4][c-4] == chess) {
                    return chess == 1 ? user1.getUserId() : user2.getUserId();
                }
            }catch (ArrayIndexOutOfBoundsException e) {
                continue;
            }
        }
        //胜负未分
        return 0;
    }

    public Room() {
        //使用UUID算法   所谓的UUID 就表示 世界上唯一的身份标识
        //构造Room的时候生成一个唯一的字符串表示房间id
        //使用UUID 来作为房间的id
        roomId = UUID.randomUUID().toString();

        //通过入口类中记录的 context 来手动获取到前面的 OnlineUserManager 和  RoomManager
        onlineUserManager = JavaGobangApplication.context.getBean(OnlineUserManager.class);
        roomManager = JavaGobangApplication.context.getBean(RoomManager.class);
        userMapper = JavaGobangApplication.context.getBean(UserMapper.class);
    }
}
